home *** CD-ROM | disk | FTP | other *** search
/ Wolfenstein 3D & Blake S… Gold (Companion Edition) / Wolfenstein 3D and Blake Stone Aliens of Gold - Companion Edition.iso / blake / blakmp / blakemap.c next >
C/C++ Source or Header  |  1993-12-11  |  71KB  |  2,698 lines

  1. /****************
  2. * Program name : BLAKEMAP.C
  3. * Description  : Blake Stone AOG mapping utility.
  4. * Version      : 1.0
  5. *
  6. * Author       : David Lummis
  7. * Compuserve Id: 71534,3067
  8. *
  9. * Last updated : Sun Dec 12, 1993 @ 00:11:53
  10. *****************/
  11.  
  12.  
  13. /******************************************************************************
  14.  
  15. MAPHEAD.BS?   Map header file: Contains file offsets to level headers
  16. MAPTEMP.BS?   Version 1.0  map data file.
  17.  
  18. Shareware version.....: MAPHEAD.BS1, MAPTEMP.BS1
  19.  
  20. If bought episodes 1-3: MAPHEAD.BS3, MAPTEMP.BS3 (contains 1 to 3)
  21.  
  22. If bought episodes 1-6: MAPHEAD.BS6, MAPTEMP.BS6 (contains 1 to 6)
  23.  
  24. Note: At the time this program was written it was not know what filename
  25.       (MAPTEMP or GAMEMAPS) will be used for later or commercial versions.
  26.       Wolfenstein 3d used GAMEMAPS starting with v1.1, and MAPTEMP for v1.0.
  27.       This program currently will check for each name starting first with
  28.       MAPTEMP and then GAMEMAPS.
  29.  
  30. ------------------------------------------------------------------------------
  31.  
  32. File: MAPHEAD.BS?  (Blake Stone AOG - v1.0)
  33. =================
  34.  
  35. File Structure (all values shown in hex):
  36.  
  37.   A) Repeat_code
  38.  
  39.       File  Data Sample
  40.      Offset Type  Data  Description
  41.      ------ ---- ------ -------------------
  42.       0000  int  CD AB  This is the 2 byte integer used in the map file to
  43.                         indicate data compression.
  44.  
  45.   B) Level_header_offsets
  46.  
  47.      For every level in each episode there is a 4 byte (ie. long) absolute
  48.      file offset which points into the map data file at a particular
  49.      Level_header.
  50.  
  51.      Each episode consists of 11 levels, one secret and 10 regular.
  52.  
  53.      All remaining offsets are FFFFFFFF or 00000000.
  54.  
  55.      Note: Shareware v1.0 contains 13 offsets, the last 2 of which
  56.            are invalid (ie. they don't point to any map data).  These
  57.            seem to be left over data which were not replaced with
  58.            FFFFFFFF's before the game was released.
  59.  
  60. ------------------------------------------------------------------------------
  61.  
  62. File: MAPTEMP.BS?  (Blake Stone AOG - version 1.0)
  63. =================
  64.  
  65. Uses compression method 0.
  66.  
  67.  
  68. File Structure (all values shown in hex):
  69.  
  70.   A) File_header
  71.  
  72.       File  Data
  73.      Offset Type Sample Data Description
  74.      ------ ---- ----------- -------------------------------------------
  75.       0000  char  "TED5v1.0" Ascii string (8 bytes) (no null terminator)
  76.  
  77.   B) Level_data (contains data for all ten levels)
  78.  
  79.      Level structure (one per level)
  80.  
  81.         a) Level_header
  82.  
  83.            Level
  84.            Header  Data
  85.            Offset  Type  Description
  86.            ------  ----  -------------------------------
  87.             0000   long  File offset of Map_Block
  88.             0004   long  File offset of Object_Block
  89.             0008   long  Unknown
  90.             000C   int   File size   of Map_Block
  91.             000E   int   File size   of Object_Block
  92.             0010   int   Unknown
  93.             0012   int   Horizontal map size (# of squares)
  94.             0014   int   Vertical   map size (# of squares)
  95.             0016   char  Level name (ascii) (null terminated)
  96.  
  97.         b) Map_block
  98.         c) Object_block
  99.         d) Unknown_block
  100.  
  101.            Each block consists of a 4 byte header followed by data.
  102.  
  103.            Block  Data
  104.            Offset Type Description
  105.            ------ ---- --------------------------------------------
  106.             0000  int  # of data bytes after decompresssing.
  107.             0002  int  Data bytes.
  108.                        Values are stored as 2 byte integers (low byte first).
  109.  
  110.                        If value == ABCD (ie. the integer we found at the
  111.                        beginning of the MAPHEAD file) then:
  112.                           - the next integer is # of repetitions
  113.                           - the next integer is value to repeat
  114.  
  115.                        If value is not ABCD then store the value as is.
  116.  
  117.         e) End_of_data
  118.  
  119.            Consists of four ascii characters:  !ID!
  120.  
  121. ------------------------------------------------------------------------------
  122.  
  123. Compression method 1:
  124.  
  125. This method was used by Wolfenstein 3D v1.1+, and the code has been left
  126. in this program.
  127.  
  128.         a) Map_block
  129.         b) Object_block
  130.         c) Unknown_block
  131.  
  132.            Each block consists of a 4 byte header followed by data.
  133.  
  134.            Block  Data
  135.            Offset Type Description
  136.            ------ ---- --------------------------------------------
  137.             0000  int  # of data bytes after decoding (count includes
  138.                        the two bytes at Block Offset 0002).
  139.             0002  int  # of data bytes after decompresssing.
  140.             0004  int  Data bytes.
  141.                        Values are stored as 2 byte integers (low byte first).
  142.  
  143.  
  144.                        To decode the data you must make two passes at it.
  145.  
  146.  
  147.                        Pass1 (decoding):
  148.                        -----
  149.  
  150.                        If high byte of value is A7 then:
  151.  
  152.                           If low byte is 00 then:
  153.                              - high byte of value is A7
  154.                              - low  byte of value is the next byte
  155.                           Else
  156.                              - the low  byte is a count (ie. # of integers we
  157.                                want to reuse).
  158.                              - the next byte is the # of integers we want
  159.                                to move back in order to reuse data we have
  160.                                already decoded.
  161.  
  162.                        If high byte of value is A8 then:
  163.  
  164.                           If low byte is 00 then:
  165.                              - high byte of value is A8
  166.                              - low  byte of value is the next byte
  167.                           Else
  168.                              - the low  byte is a count (ie. # of integers we
  169.                                want to reuse).
  170.                              - the next integer (2 bytes) is the # of integers
  171.                                we want to skip over, starting at the beginning
  172.                                of the buffer being used to hold data we have
  173.                                already decoded (the first integer in the buffer
  174.                                being the # of bytes after decompressing).
  175.  
  176.                        If high byte is not A7 or A8, then store the value
  177.                        as is.
  178.  
  179.  
  180.                        Pass2 (decompressing):
  181.                        -----
  182.  
  183.                        If value == ABCD (ie. the integer we found at the
  184.                        beginning of the MAPHEAD file) then:
  185.                           - the next integer is # of repetitions
  186.                           - the next integer is value to repeat
  187.  
  188.                        If value is not ABCD then store the value as is.
  189.  
  190.  
  191. ******************************************************************************/
  192.  
  193.  
  194. #include <stdio.h>
  195. #include <stdlib.h>
  196. #include <string.h>
  197. #include <ctype.h>
  198. #include <dos.h>
  199.  
  200.  
  201. #define byte unsigned char
  202. #define word unsigned int
  203.  
  204. // Maximum number of levels allowed for in each game's map file.
  205. #define MAP_MAXLEVELS 11
  206.  
  207. // Maximum number of episodes allowed
  208. #define MAP_MAXEPISODES 6
  209.  
  210. // Filenames
  211. #define FILE_MAPEXT   ".BS"
  212. #define FILE_MAPHEAD  "MAPHEAD"
  213. #define FILE_0MAPTEMP "MAPTEMP"
  214. #define FILE_1MAPTEMP "GAMEMAPS"
  215.  
  216. #define FILE_LEGEND    "BMAP"
  217. #define FILE_LEGENDEXT ".LEG"
  218.  
  219. #define FILE_COUNT    "BMAP"
  220. #define FILE_COUNTEXT ".COU"
  221.  
  222. #define FILE_MAP      "BMAP"
  223. #define FILE_MAP2     "BMAP"
  224. #define FILE_BLAKEMAP "BLAKEMAP.CFG"
  225.  
  226. // Number of different map types generated by this program
  227. #define MAP_NUMTYPES   3
  228.  
  229. // Section names allowed in the BLAKEMAP configuration file.
  230. #define SECT_MAPGROUPS "[MAP GROUPS]"
  231. #define SECT_MAPVALUES "[MAP VALUES]"
  232. #define SECT_OBJGROUPS "[OBJECT GROUPS]"
  233. #define SECT_OBJVALUES "[OBJECT VALUES]"
  234.  
  235. // Minimum lengths of GROUP and VALUE lines in the BLAKEMAP configuration file.
  236. #define LEN_GROUPLINE 11
  237. #define LEN_VALUELINE 6
  238.  
  239. // Max length of group descriptions
  240. #define LEN_GDESCRIPTION 35
  241.  
  242. // Minimum and maximum GROUP and VALUE numbers.
  243. // Total number of GROUPS and VALUES allowed.
  244. #define MIN_GROUP    0
  245. #define MIN_VALUE    0
  246. #define MAX_GROUP  255
  247. #define MAX_VALUE  511
  248. #define NUM_GROUPS 256
  249. #define NUM_VALUES 512
  250. // total # of map and object groups
  251. #define NUM_GROUPS2 512
  252.  
  253. // Map group types
  254. #define MT_OTHER 0
  255. #define MT_WALL  1
  256. #define MT_DOOR  2
  257. #define MT_FLOOR 3
  258.  
  259. // Object group types
  260. #define OT_OTHER      0
  261. #define OT_TRIVIAL    1
  262. #define OT_NONTRIVIAL 2
  263. #define OT_ENEMY      3
  264.  
  265. // Maximum X and Y values allowed for maps.
  266. // Maximum size (in bytes) needed to hold fully uncompressed map data.
  267. // Maximum size (in words) needed to hold fully uncompressed map data.
  268. #define MAX_X 64
  269. #define MAX_Y 64
  270. #define MAX_BYTES 8192
  271. #define MAX_WORDS 4096
  272.  
  273. char nullstr[1];
  274.  
  275. struct find_t diskinfo;
  276.  
  277. int  episode_num;      // Game number (1 to 6)
  278. int  num_episodes;     // Number of episodes found in MAPHEAD file.
  279. int  comp_method;      // Compression method used (0=Wolf v1.0, 1=Wolf v1.1+)
  280. int  user_comp_method; // User override of compression method via command line
  281.  
  282. // Filenames
  283. char data_extension[5];       // Extension used for data files (eg ".BS1")
  284. char maphead_filename[255];   // MAPHEAD filename (map offsets)
  285. char maptemp_filename[255];   // MAPTEMP filename (map data)
  286. char blakemap_filename[255];  // Holds name of BLAKEMAP's configuration file
  287. char count_filename[255];     // Name of file to contain data counts.
  288. char legend_filename[255];    // Name of file to contain map symbol legend.
  289.  
  290. char map_name[255];
  291. char obj_name[255];
  292. char comb_name[255];
  293.  
  294. int ep11_flag;  // =1 if we found episode 1-1 file
  295. int ep13_flag;  // =1 if we found episode 1-3 file
  296. int ep16_flag;  // =1 if we found episode 1-6 file
  297.  
  298. struct group_rec       // Configuration file GROUP record layout
  299. {
  300.    byte character;        // Character to use when creating maps
  301.    byte character2a;      // Character to use when creating maps (/2 option)
  302.    byte character2b;      // Character to use when creating maps (/2 option)
  303.    int  type;             // Group type number
  304.    char legend;           // Y = print on legend page
  305.    char *description;     // Pointer to description
  306. };
  307.  
  308. struct value_rec       // Configuration file VALUE record layout
  309. {
  310.    int  group;            // Group number
  311. };
  312.  
  313. struct group_rec map_groups[NUM_GROUPS];
  314. struct value_rec map_values[NUM_VALUES];
  315. struct group_rec obj_groups[NUM_GROUPS];
  316. struct value_rec obj_values[NUM_VALUES];
  317.  
  318. struct group_rec *sortgroups[NUM_GROUPS2];
  319.  
  320.  
  321. char input_buffer[255];   // Used when reading configuration file and user input
  322. int  input_line;          // Current line number in configuration file.
  323. long input_pos;           // Used to hold current file offset in config file.
  324.  
  325. word code_repeat;    // Holds repeat code found at start of MAPHEAD file
  326.  
  327.  
  328. word counts[MAP_MAXLEVELS][512];  // Holds data counts by level
  329. word totals[512];
  330.  
  331.  
  332. int  help_flag;
  333. int  item_mode;
  334. int  count_flag;
  335. int  enemy_mode;
  336. int  pause_flag;
  337. int  num_lines;
  338. int  map_type;         // Type of map to generate.
  339. int  double_width;     // 1=Use 2 characters to represent a map/object value
  340. int  map_from;         // Starting level number
  341. int  map_to;           // Ending level number
  342. char map_title[255];   // Title for current map
  343. int  hex_scale_x;      // 1=print hexadecimal X scale on map
  344. int  hex_scale_y;      // 1=print hexadecimal Y scale on map
  345.  
  346. unsigned long map_offset[MAP_MAXEPISODES][MAP_MAXLEVELS]; // Offsets of map data in maptemp file
  347.  
  348.  
  349. word map_data[MAX_WORDS];    // Fully uncompressed map data
  350. word obj_data[MAX_WORDS];    // Fully uncompressed object data
  351. byte buf1_data[MAX_BYTES];   // Holds data read directly from file
  352. word buf2_data[MAX_WORDS];   // Holds decoded data
  353.  
  354.  
  355. struct level_header
  356. {
  357.    long map_off;   // Offset of map data block
  358.    long obj_off;   // Offset of object data block
  359.    long xxx_off;   // Unknown
  360.    word map_size;  // Size of map data block (before decompressing)
  361.    word obj_size;  // Size of object data block (before decompressing)
  362.    word xxx_size;  // Unknown
  363.    word xsize;     // # of horizontal squares
  364.    word ysize;     // # of vertical squares
  365.    char name[16];  // Map name
  366. };
  367.  
  368. struct level_header lh;
  369.  
  370.  
  371.  
  372. /****
  373. * Function prototypes
  374. ****/
  375. void read_config();
  376. void init_section(FILE *hConfig, char *p1);
  377. char *init_readline(FILE *handle);
  378. void init_groups(struct group_rec *group, FILE *hConfig);
  379. void init_values(struct value_rec *value, FILE *hConfig);
  380. void set_options(char *f);
  381.  
  382. void one_more_line();
  383. void pause(char *p);
  384. void display_help();
  385.  
  386. int  make_map(int level, char *mapfile, char *objfile, char *combfile);
  387. int  make_map2(int which, int level, char *outfile, word map_info[]);
  388. int  read_map(int level);
  389. void read_bytes(FILE *in_stream, word map_info[], word data_size);
  390. int  seek_file(FILE *stream, long offset);
  391. void print_xscale(FILE *out_stream, word xsize, int map_type);
  392. void print_bytes(int which, FILE *out_stream, word map_info[]);
  393.  
  394. int  count_data();
  395. void print_counts(FILE *out_stream);
  396. void count_level(int level, word map_info[]);
  397.  
  398. int hextoi(char *hex);
  399.  
  400. void print_legend();
  401. void vSort_List(struct group_rec *apsList[], int iLength);
  402.  
  403. int  read_hdr_info(FILE *stream, int max_episodes);
  404. void read_header_files();
  405.  
  406. /*--------------------------------------------------------------------------*/
  407.  
  408.  
  409. main(int argc, char *argv[])
  410. {
  411.    char *p1;
  412.    int i, count;
  413.    FILE *temp;
  414.    int eplow, ephi;
  415.  
  416.    nullstr[0] = NULL;
  417.  
  418.    /******
  419.    * Initialize defaults
  420.    ******/
  421.    episode_num = 0;  // Episode number (0 unless user uses /G option)
  422.    num_episodes= 1;  // # of episodes
  423.  
  424.    comp_method = 0;       // 0=v1.0
  425.    user_comp_method = -1; // -1 = no user override
  426.  
  427.    help_flag  = 0;   // 0=No help
  428.    item_mode  = 2;   // 2=Show non-trivial items only
  429.    count_flag = 0;   // 0=No count file
  430.    enemy_mode = 1;   // 1=Enemies by skill level
  431.    pause_flag = 1;   // 1=Pause during help screen.
  432.    num_lines  = 0;   // Line counter for pause option
  433.  
  434.    map_type     = 0; // Normal map
  435.    double_width = 0; // 0=use a single char on map for each map/object value
  436.    map_from     = 0; // From level (0 unless user uses /L option)
  437.    map_to       = 0; // To   level (0 unless user uses /L option)
  438.  
  439.    hex_scale_x  = 1; // 1=print hexadecimal X scale on map
  440.    hex_scale_y  = 1; // 1=print hexadecimal Y scale on map
  441.  
  442.    ep11_flag  = 0;
  443.    ep13_flag  = 0;
  444.    ep16_flag  = 0;
  445.  
  446.    for (i=0; i<MAP_MAXEPISODES; i++)
  447.    {
  448.       for (count=0; count < MAP_MAXLEVELS; count++)
  449.          map_offset[i][count] = 0;
  450.    }
  451.  
  452.    *maphead_filename  = NULL;
  453.    *maptemp_filename  = NULL;
  454.    *blakemap_filename = NULL;
  455.    *count_filename    = NULL;
  456.  
  457.  
  458.    /******
  459.    * Print opening title
  460.    ******/
  461.    printf("\nBLAKEMAP v1.0 by David Lummis.  For a help summary use: BLAKEMAP /?\n\n");
  462.              
  463.    /******
  464.    * Check for command line arguments
  465.    ******/
  466.    if (argc > 1)
  467.    {
  468.       for(count=1; count<argc; count++)
  469.       {
  470.          if (*argv[count]=='-' || *argv[count]=='/')
  471.             set_options(argv[count]+1);
  472.       }
  473.    }
  474.  
  475.    /******
  476.    * Display help if Help flag is on
  477.    *******/
  478.    if (help_flag)
  479.    {
  480.       display_help();
  481.       return;
  482.    }
  483.  
  484.    /******
  485.    * Read data from the map header file(s) and determine the # of episodes
  486.    * available.
  487.    *******/
  488.    read_header_files();
  489.  
  490.  
  491.    /******
  492.    * Read and process the configuration file
  493.    ******/
  494.    if (*blakemap_filename==NULL)                 // If user did not supply name
  495.       strcpy(blakemap_filename, FILE_BLAKEMAP);  // use default filename
  496.    read_config();
  497.  
  498.  
  499.    /******
  500.    *
  501.    * Found episodes: 1-3
  502.    *
  503.    * Episode number  (1 to 6)?
  504.    * Level # (0 to 10, *=All)?
  505.    *
  506.    * If user did not specify a game number (and there is more than 1 game
  507.    * available) or if user did not specify a level number,
  508.    * then ask user interactively.
  509.    ******/
  510.    printf("Found episodes: ");
  511.  
  512.    if (ep16_flag)
  513.    {
  514.       printf("1-6");
  515.       eplow = 1;
  516.       ephi  = 6;
  517.    }
  518.    else if (ep11_flag)
  519.    {
  520.       printf("1");
  521.       eplow = 1;
  522.       ephi  = 1;
  523.    }
  524.    else if (ep13_flag)
  525.    {
  526.       printf("1-3");
  527.       eplow = 1;
  528.       ephi  = 3;
  529.    }
  530.    else
  531.    {
  532.       printf("1-%s", num_episodes);
  533.       eplow = 1;
  534.       ephi  = num_episodes;
  535.    }
  536.    printf("\n\n");
  537.  
  538.  
  539.    if (ephi-eplow > 1 && episode_num==0)
  540.    {
  541.       while (episode_num == 0)
  542.       {
  543.          printf("Episode number  (%d to %d)? ", eplow, ephi);
  544.          gets(input_buffer);
  545.  
  546.          episode_num = atoi(input_buffer);
  547.          if (episode_num==0)
  548.             exit(0);
  549.  
  550.          if (episode_num < eplow || episode_num > ephi || episode_num > MAP_MAXEPISODES)
  551.             episode_num = 0;
  552.       }
  553.    }
  554.    else
  555.    {
  556.       if (episode_num < eplow || episode_num > ephi || episode_num > MAP_MAXEPISODES)
  557.          episode_num = 1;
  558.  
  559.       printf("Episode number..........: %d\n", episode_num);
  560.    }
  561.  
  562.    if (map_from==0 || map_to==0)
  563.    {
  564.       while (map_from == 0)
  565.       {
  566.          printf("Level # (0 to %2d, *=All)? ", MAP_MAXLEVELS-1);
  567.          gets(input_buffer);
  568.          if (*input_buffer=='*')
  569.          {
  570.             map_from = 1;
  571.             map_to   = MAP_MAXLEVELS;
  572.          }
  573.          else
  574.          {
  575.             map_from = atoi(input_buffer);
  576.             map_from++;
  577.  
  578.             if (map_from == 0)
  579.                exit(0);
  580.  
  581.             if (map_from >= 1 && map_from <= MAP_MAXLEVELS)
  582.                map_to = map_from;
  583.             else
  584.                map_from = 0;
  585.          }
  586.       }
  587.    }
  588.    else
  589.    {
  590.       if (map_from==map_to)
  591.          printf("Level number............: %d\n", map_from-1);
  592.       else
  593.          printf("Level numbers...........: %d to %d\n", map_from-1, map_to-1);
  594.    }
  595.    printf("\n");
  596.  
  597.  
  598.    /******
  599.    * Look for Blake Stone map data file.
  600.    *
  601.    *   1. Look for MAPTEMP<data_extension>.   Uses compression method 0.
  602.    *   2. Look for GAMEMAPS<data_extension>.  Uses compression method 1.
  603.    ******/
  604.  
  605.    /* Extract extension from MAPHEAD filename */
  606.    p1 = strpbrk(maphead_filename, ".");
  607.    if (p1)
  608.       strcpy(data_extension, p1);
  609.    else
  610.       *data_extension = NULL;
  611.  
  612.    strcpy(maptemp_filename, FILE_0MAPTEMP);
  613.    strcat(maptemp_filename, data_extension);
  614.  
  615.    comp_method = 0;  // v1.0
  616.  
  617.    temp = fopen(maptemp_filename, "rb");
  618.    if (temp==NULL)
  619.    {
  620.       strcpy(maptemp_filename, FILE_1MAPTEMP);
  621.       strcat(maptemp_filename, data_extension);
  622.  
  623.       comp_method = 1;  // v1.1
  624.  
  625.       temp = fopen(maptemp_filename, "rb");
  626.       if (temp==NULL)
  627.       {
  628.          printf("Unable to open Blake Stone AOG map data file.\n");
  629.          exit(1);
  630.       }
  631.    }
  632.    fclose(temp);
  633.  
  634.    /* Allow user specified decompression method to override computer's choice */
  635.    if (user_comp_method >= 0)
  636.       comp_method = user_comp_method;
  637.  
  638.  
  639.    /* The count-file filename */
  640.    sprintf(count_filename, "%s%d%s", FILE_COUNT, episode_num, FILE_COUNTEXT);
  641.  
  642.  
  643.    /******
  644.    * Now we generate the maps
  645.    ******/
  646.    for (i=map_from-1; i<map_to && i<MAP_MAXLEVELS; i++)
  647.    {
  648.       //Creating map for Episode 1, Level 10...xxxxxxxx.xxx, xxxxxxxx.xxx, xxxxxxxx.xxx
  649.       printf("Creating map for Episode %d, Level %2d...", episode_num, i);
  650.  
  651.       /* Create filenames */
  652.       if (map_type==0)
  653.       {
  654.          sprintf(comb_name, "%s%d.L%02d", FILE_MAP, episode_num, i);
  655.       }
  656.       else
  657.       {
  658.          sprintf(comb_name, "%s%dL%02d.T%dA", FILE_MAP2, episode_num, i, map_type);
  659.          sprintf(map_name,  "%s%dL%02d.T%dB", FILE_MAP2, episode_num, i, map_type);
  660.          sprintf(obj_name,  "%s%dL%02d.T%dC", FILE_MAP2, episode_num, i, map_type);
  661.       }
  662.  
  663.       if (map_offset[episode_num-1][i]!=0 && map_offset[episode_num-1][i]!=0xffffffff)
  664.       {
  665.          /* Generate the map */
  666.          if (make_map(i, map_name, obj_name, comb_name))
  667.             break;
  668.  
  669.          if (map_type==0)
  670.             printf(" Filename = %s", comb_name);
  671.          else
  672.          {
  673.             printf("%s, %s, %s", comb_name, map_name, obj_name);
  674.          }
  675.       }
  676.       else
  677.          printf("Invalid file offset.");
  678.  
  679.       /* Drop cursor onto next screen line */
  680.       printf("\n");
  681.    }
  682.  
  683.    /******
  684.    * If user wants a count file, then create it now.
  685.    ******/
  686.    if (count_flag)
  687.       count_data();
  688.  
  689.    /******
  690.    * Print legend page
  691.    ******/
  692.    if (map_type==0 || map_type==1)
  693.       print_legend();
  694. }
  695.  
  696.  
  697. /*---------------------------------------------------------------------------*/
  698.  
  699.  
  700. void read_config()
  701. /*******************
  702. * Read and process the configuration file
  703. *******************/
  704. {
  705.    FILE *hConfig;
  706.    char *p2;
  707.    int  count;
  708.  
  709.    /***
  710.    * Initialize to default values
  711.    ***/
  712.    for (count=MIN_GROUP; count<=MAX_GROUP; count++)
  713.    {
  714.       map_groups[count].character   = '?';
  715.       map_groups[count].character2a = '?';
  716.       map_groups[count].character2b = '?';
  717.       map_groups[count].type        = 0;
  718.       map_groups[count].legend      = 'N';
  719.       map_groups[count].description = nullstr;
  720.  
  721.       obj_groups[count].character   = '?';
  722.       obj_groups[count].character2a = '?';
  723.       obj_groups[count].character2b = '?';
  724.       obj_groups[count].type        = 0;
  725.       obj_groups[count].legend      = 'N';
  726.       obj_groups[count].description = nullstr;
  727.    }
  728.    for (count=MIN_VALUE; count<=MAX_VALUE; count++)
  729.    {
  730.       map_values[count].group = 0;
  731.  
  732.       obj_values[count].group = 0;
  733.    }
  734.  
  735.    /***
  736.    * Open configuration file.
  737.    ***/
  738.    hConfig = fopen(blakemap_filename, "r");
  739.    if (hConfig == NULL)
  740.    {
  741.       printf("Error opening %s\n", blakemap_filename);
  742.       exit(1);
  743.    }
  744.  
  745.    /***
  746.    * Read and process config file
  747.    ***/
  748.    input_line = 0;
  749.  
  750.    p2 = init_readline(hConfig);   // Read first line
  751.  
  752.    while (feof(hConfig)==0)
  753.    {
  754.       switch (*p2)
  755.       {
  756.          case '[':
  757.             init_section(hConfig, p2);  // Handle a section
  758.             break;
  759.  
  760.          default:
  761.             printf("File %s, Line %d: Unknown line encountered.\n", blakemap_filename, input_line);
  762.             break;
  763.       }
  764.  
  765.       p2 = init_readline(hConfig);      // Read next line
  766.    }
  767.  
  768.    fclose(hConfig);
  769. }
  770.  
  771.  
  772. /*---------------------------------------------------------------------------*/
  773.  
  774.  
  775. void init_section(FILE *hConfig, char *p1)
  776. /********
  777. * Process a section in the config file.
  778. ********/
  779. {
  780.    char *p2;
  781.  
  782.    p2 = strpbrk(p1, "]");
  783.    if (p2 == NULL)
  784.    {
  785.       printf("File %s, Line %d: Invalid section header.\n", blakemap_filename, input_line);
  786.       exit(1);
  787.    }
  788.  
  789.    *(p2+1) = NULL;
  790.  
  791.    if (strcmp(p1, SECT_MAPGROUPS)==0)
  792.    {
  793.       init_groups(map_groups, hConfig);
  794.    }
  795.    else if (strcmp(p1, SECT_MAPVALUES)==0)
  796.    {
  797.       init_values(map_values, hConfig);
  798.    }
  799.    else if (strcmp(p1, SECT_OBJGROUPS)==0)
  800.    {
  801.       init_groups(obj_groups, hConfig);
  802.    }
  803.    else if (strcmp(p1, SECT_OBJVALUES)==0)
  804.    {
  805.       init_values(obj_values, hConfig);
  806.    }
  807.    else
  808.    {
  809.       printf("File %s, Line %d: Unknown section header encountered.\n", blakemap_filename, input_line);
  810.       exit(1);
  811.    }
  812.  
  813.    return;
  814. }
  815.  
  816.  
  817. /*---------------------------------------------------------------------------*/
  818.  
  819.  
  820. char *init_readline(FILE *handle)
  821. /********
  822. * Read a line from the config file
  823. ********/
  824. {
  825.    char *p2;
  826.  
  827.    while (feof(handle)==0)
  828.    {
  829.       input_pos = ftell(handle);          // Save current file position
  830.       fgets(input_buffer, 254, handle);   // Read line
  831.  
  832.       input_line++;                       // Increment line counter
  833.  
  834.       p2 = strpbrk(input_buffer, "\n");   // Check for newline char
  835.       if (p2)
  836.          *p2 = NULL;                      // Truncate at newline char
  837.  
  838.       p2 = input_buffer;
  839.       while (*p2==' ')
  840.          p2++;                            // Skip leading blanks
  841.  
  842.       if (*p2 != '*' && *p2 != NULL)      // Exit if not null or comment
  843.          break;
  844.    }
  845.  
  846.    return(p2);
  847. }
  848.  
  849.  
  850. /*---------------------------------------------------------------------------*/
  851.  
  852.  
  853. void init_groups(struct group_rec *group, FILE *hConfig)
  854. /**********
  855. * Read group info from configuration file
  856. **********/
  857. {
  858.    byte work[4];
  859.    byte *p2, *desc;
  860.    int  group_num;
  861.    byte character;
  862.    byte character2a;
  863.    byte character2b;
  864.    int  type;
  865.    char legend;
  866.  
  867.    work[3] = NULL;
  868.  
  869.    p2 = init_readline(hConfig);    // Read first line
  870.  
  871.    while (feof(hConfig)==0 && *p2 != '[')
  872.    {
  873.       // Make sure line is long enough
  874.       if (strlen(p2) < LEN_GROUPLINE)
  875.       {
  876.          printf("File %s, Line %d: Not enough characters on line.\n", blakemap_filename, input_line);
  877.          exit(1);
  878.       }
  879.  
  880.       // Extract "group number"
  881.       work[0] = *p2++;
  882.       work[1] = *p2++;
  883.       work[2] = *p2++;
  884.       group_num = atoi(work);
  885.       if (group_num < 0 || group_num > 255)
  886.       {
  887.          printf("File %s, Line %d: Group number out of range.\n", blakemap_filename, input_line);
  888.          exit(1);
  889.       }
  890.  
  891.       p2++;   // Skip over space
  892.  
  893.       // Extract "character"
  894.       character = *p2++;
  895.  
  896.       p2++;   // Skip over space
  897.  
  898.       // Extract "double width character 1"
  899.       character2a = *p2++;
  900.       // Extract "double width character 2"
  901.       character2b = *p2++;
  902.  
  903.       p2++;   // Skip over space
  904.  
  905.       // Extract "type"
  906.       work[0] = *p2++;
  907.       work[1] = NULL;
  908.       type = atoi(work);
  909.       if (type < 0 || type > 255)
  910.       {
  911.          printf("File %s, Line %d: Type number out of range.\n", blakemap_filename, input_line);
  912.          exit(1);
  913.       }
  914.  
  915.       p2++;   // Skip over space
  916.  
  917.       // Extract "print on legend?" flag (Y=print)
  918.       legend = toupper(*p2);
  919.       if (legend != 'Y')
  920.          legend = 'N';
  921.       p2++;
  922.  
  923.       /* Rest of line may be missing, so test char at p2 before using */
  924.       if (*p2)
  925.          p2++;   // Skip over space
  926.  
  927.       /* Allocate memory for description */
  928.       desc = malloc(LEN_GDESCRIPTION+1);
  929.       if (desc==NULL)
  930.       {
  931.          printf("Unable to allocate memory for group description.\n");
  932.          exit(1);
  933.       }
  934.  
  935.       if (*p2)
  936.       {
  937.          strncpy(desc, p2, LEN_GDESCRIPTION);
  938.          *(desc+LEN_GDESCRIPTION)=NULL;        // Make sure null terminated.
  939.       }
  940.       else
  941.          *desc = NULL;
  942.  
  943.       // Assign values
  944.       group[group_num].character   = character;
  945.       group[group_num].character2a = character2a;
  946.       group[group_num].character2b = character2b;
  947.       group[group_num].type        = type;
  948.       group[group_num].legend      = legend;
  949.       group[group_num].description = desc;
  950.  
  951.       p2 = init_readline(hConfig); // Read next line
  952.    }
  953.  
  954.    if (*p2 == '[')
  955.    {
  956.       fseek(hConfig, input_pos, SEEK_SET); // Go back to the "[" line
  957.       input_line--;
  958.    }
  959.  
  960.    return;
  961. }
  962.  
  963.  
  964. /*---------------------------------------------------------------------------*/
  965.  
  966.  
  967. void init_values(struct value_rec *value, FILE *hConfig)
  968. /**********
  969. * Read value info from configuration file
  970. **********/
  971. {
  972.    char work[4];
  973.    char *p2;
  974.    int  value_num;
  975.    int  group_num;
  976.  
  977.    work[3] = NULL;
  978.  
  979.    p2 = init_readline(hConfig); // Read first line
  980.  
  981.    while (feof(hConfig)==0 && *p2 != '[')
  982.    {
  983.       // Make sure line is long enough
  984.       if (strlen(p2) < LEN_VALUELINE)
  985.       {
  986.          printf("File %s, Line %d: Not enough characters on line.\n", blakemap_filename, input_line);
  987.          exit(1);
  988.       }
  989.  
  990.       // Extract "value number"
  991.       work[0] = *p2++;
  992.       work[1] = *p2++;
  993.       work[2] = *p2++;
  994.       work[3] = NULL;
  995.       value_num = hextoi(work);
  996.       if (value_num < 0 || value_num > 511)
  997.       {
  998.          printf("File %s, Line %d: Value out of range.\n", blakemap_filename, input_line);
  999.          exit(1);
  1000.       }
  1001.  
  1002.       p2++;   // Skip over space
  1003.  
  1004.       // Extract "group number"
  1005.       work[0] = *p2++;
  1006.       work[1] = *p2++;
  1007.       work[2] = *p2++;
  1008.       group_num = atoi(work);
  1009.       if (group_num < 0 || group_num > 255)
  1010.       {
  1011.          printf("File %s, Line %d: Group number out of range.\n", blakemap_filename, input_line);
  1012.          exit(1);
  1013.       }
  1014.  
  1015.       // Assign values
  1016.       value[value_num].group = group_num;
  1017.  
  1018.       p2 = init_readline(hConfig); // Read next line
  1019.    }
  1020.  
  1021.    if (*p2 == '[')
  1022.    {
  1023.       fseek(hConfig, input_pos, SEEK_SET); // Go back to the "[" line
  1024.       input_line--;
  1025.    }
  1026.  
  1027.    return;
  1028. }
  1029.  
  1030.  
  1031. /*---------------------------------------------------------------------------*/
  1032.  
  1033.  
  1034. void set_options(char *f)
  1035. {
  1036.  int data, data2, on_off;
  1037.  char *p;
  1038.  byte ch;
  1039.  
  1040.  while( *f != NULL)
  1041.  {
  1042.     data = toupper(*f);
  1043.  
  1044.     on_off = 1;
  1045.     data2  = NULL;
  1046.     if (data)
  1047.     {
  1048.        /* Check next char for + or -. (quick check for allowing on/off switches) */
  1049.        data2 = *(f+1);
  1050.        switch(data2)
  1051.        {
  1052.           case '+':
  1053.              on_off = 1;
  1054.              break;
  1055.           case '-':
  1056.              on_off = 0;
  1057.              break;
  1058.           default:
  1059.              on_off = 1;
  1060.              break;
  1061.        }
  1062.     }
  1063.  
  1064.     switch (data)
  1065.     {
  1066.      case '?':
  1067.      case 'H':
  1068.         help_flag = 1;     /* Display help screen */
  1069.  
  1070.         pause_flag = on_off;
  1071.         return;
  1072.  
  1073.      case '2':
  1074.         double_width = on_off;
  1075.         return;
  1076.  
  1077.      case 'C':
  1078.         count_flag = on_off;
  1079.         return;
  1080.  
  1081.      case 'D':
  1082.         f++;
  1083.  
  1084.         switch (*f)
  1085.         {
  1086.          case '0':
  1087.             user_comp_method = 0;
  1088.             break;
  1089.          case '1':
  1090.             user_comp_method = 1;
  1091.             break;
  1092.          default:
  1093.             user_comp_method = 0;
  1094.             break;
  1095.         }
  1096.         return;
  1097.  
  1098.      case 'F':
  1099.         f++;
  1100.  
  1101.         data2 = toupper(*f);
  1102.  
  1103.         if (data2)
  1104.         {
  1105.            f++;
  1106.            switch (data2)
  1107.            {
  1108.               case 'C':
  1109.                  strcpy(blakemap_filename, f);
  1110.                  break;
  1111.            }
  1112.         }
  1113.         return;
  1114.  
  1115.      case 'X':
  1116.         f++;
  1117.  
  1118.         if (*f=='-')
  1119.            hex_scale_x = 0;
  1120.         else
  1121.            hex_scale_x = 1;
  1122.         return;
  1123.  
  1124.      case 'Y':
  1125.         f++;
  1126.  
  1127.         if (*f=='-')
  1128.            hex_scale_y = 0;
  1129.         else
  1130.            hex_scale_y = 1;
  1131.         return;
  1132.  
  1133.      case 'L':
  1134.         f++;
  1135.  
  1136.         if (*f == '*')
  1137.         {
  1138.            map_from = 1;
  1139.            map_to   = MAP_MAXLEVELS;
  1140.         }
  1141.         else
  1142.         {
  1143.            map_from = atoi(f);
  1144.            map_from++;
  1145.  
  1146.            if (map_from < 1) map_from = 1;
  1147.            if (map_from > MAP_MAXLEVELS) map_from = MAP_MAXLEVELS;
  1148.  
  1149.            map_to   = map_from;
  1150.         }
  1151.         return;
  1152.  
  1153.      case 'T':
  1154.         f++;
  1155.  
  1156.         map_type = atoi(f);
  1157.         if (map_type < 0) map_type = 0;
  1158.         if (map_type > MAP_NUMTYPES) map_type = MAP_NUMTYPES;
  1159.  
  1160.         return;
  1161.  
  1162.      case 'G':
  1163.         f++;
  1164.  
  1165.         episode_num = atoi(f);
  1166.  
  1167.         if (episode_num < 1 || episode_num > MAP_MAXEPISODES)
  1168.            episode_num = 1;
  1169.  
  1170.         return;
  1171.  
  1172.      case 'E':
  1173.         f++;
  1174.  
  1175.         /* This applies to symbolic maps only */
  1176.         switch (*f)
  1177.         {
  1178.            case '-':
  1179.               /* suppress enemies */
  1180.               enemy_mode = 0;
  1181.               break;
  1182.         }
  1183.         return;
  1184.  
  1185.      case 'O':
  1186.         f++;
  1187.  
  1188.         switch (*f)
  1189.         {
  1190.            case '-':
  1191.               /* suppress all items */
  1192.               item_mode = 0;
  1193.               break;
  1194.  
  1195.            case '1':
  1196.               /* display all items */
  1197.               item_mode = 1;
  1198.               break;
  1199.  
  1200.            case '2':
  1201.               /* display only non trivial items */
  1202.               item_mode = 2;
  1203.               break;
  1204.         }
  1205.         return;
  1206.     }
  1207.     f++;
  1208.  }
  1209.  return;
  1210. }
  1211.  
  1212.  
  1213. /*---------------------------------------------------------------------------*/
  1214.  
  1215.  
  1216. void one_more_line()
  1217. {
  1218.  num_lines++;
  1219.  if (pause_flag && num_lines >= 23)
  1220.  {
  1221.     printf("Strike a key when ready . . . ");
  1222.     getch();
  1223.  
  1224.     printf("%c                              %c", 13, 13);
  1225.  
  1226.     num_lines = 0;
  1227.  }
  1228.  return;
  1229. }
  1230.  
  1231.  
  1232. /*---------------------------------------------------------------------------*/
  1233.  
  1234.  
  1235. void pause(char *p)
  1236. {
  1237.  puts(p);
  1238.  one_more_line();
  1239. }
  1240.  
  1241.  
  1242. /*---------------------------------------------------------------------------*/
  1243.  
  1244.  
  1245. void display_help()
  1246. {
  1247.    pause("Syntax:");
  1248.    pause(" ");
  1249.    pause("  BLAKEMAP [ <option>[ ...]]");
  1250.    pause(" ");
  1251.    pause("Valid <option>s:");
  1252.    pause(" ");
  1253.    pause("  /L<n>      Level number (0 to 10, or * for all).");
  1254.    pause("  /G<n>      Game (episode) number (1 to 6).");
  1255.    pause(" ");
  1256.    pause("  /E-        Suppress display of enemies (/T0 maps only).");
  1257.    pause(" ");
  1258.    pause("  /O1        Display all objects.");
  1259.    pause("  /O2        Display non-trivial objects only. (default)");
  1260.    pause("  /O-        Suppress all objects (/T0 maps only).");
  1261.    pause(" ");
  1262.    pause("  /2         Use two characters per item on symbolic maps (/T0 and /T1 only)");
  1263.    pause(" ");
  1264.    pause("  /T0        Map type 0: Symbolic. (default)");
  1265.    pause("  /T1        Map type 1: Symbolic.");
  1266.    pause("  /T2        Map type 2: Full hex dump.  Not affected by /O.");
  1267.    pause("  /T3        Map type 3: Partial hex dump.");
  1268.    pause(" ");
  1269.    pause("  /C         Create a file containing counts of map and object values");
  1270.    pause(" ");
  1271.    pause("  /X         Display  hexadecimal X scale on maps. (default)");
  1272.    pause("  /X-        Suppress hexadecimal X scale on maps.");
  1273.    pause("  /Y         Display  hexadecimal Y scale on maps. (default)");
  1274.    pause("  /Y-        Suppress hexadecimal Y scale on maps.");
  1275.    pause(" ");
  1276.    pause("  /FC<file>  Filename of BLAKEMAP Configuration file (default is BLAKEMAP.CFG)");
  1277.    pause(" ");
  1278.    pause("  /?         Display this help screen.");
  1279.    pause("  /?-        Display this help screen (no pausing).");
  1280.    pause(" ");
  1281.    pause("Required files:");
  1282.    pause(" ");
  1283.    pause("   The current directory is searched for 3 files:");
  1284.    pause(" ");
  1285.    pause("      1) MAPHEAD.BS?     Blake Stone AOG all versions");
  1286.    pause(" ");
  1287.    pause("      2) MAPTEMP.BS?     Blake Stone AOG v1.0");
  1288. // pause("         GAMEMAPS.BS?    Blake Stone AOG v1.1 and higher");
  1289.    pause(" ");
  1290.    pause("      3) BLAKEMAP.CFG    This is BLAKEMAP's configuration file.");
  1291.    pause(" ");
  1292.    pause("Files created:");
  1293.    pause(" ");
  1294.    pause("  Opt  Filenames         Type          Map contents");
  1295.    pause("  ---  ----------------  ------------  -------------------------------------");
  1296.    pause("  /T0  BMAP<g>.L<nn>     Symbolic.     Map and object data.");
  1297.    pause("       BMAP<g>.LEG       Legend.       List of symbols used on maps.");
  1298.    pause(" ");
  1299.    pause("  /T1  BMAP<g>L<nn>.T1A  Symbolic.     Map and object data.");
  1300.    pause("       BMAP<g>L<nn>.T1B  Symbolic.     Map data only.");
  1301.    pause("       BMAP<g>L<nn>.T1C  Symbolic.     Object data only.");
  1302.    pause("       BMAP<g>.LEG       Legend.       List of symbols used on maps.");
  1303.    pause(" ");
  1304.    pause("  /T2  BMAP<g>L<nn>.T2A  Full hex.     Map and object data.");
  1305.    pause("       BMAP<g>L<nn>.T2B  Full hex.     Map data only.");
  1306.    pause("       BMAP<g>L<nn>.T2C  Full hex.     Object data only.");
  1307.    pause(" ");
  1308.    pause("  /T3  BMAP<g>L<nn>.T3A  Partial hex.  Map and object data.  No floors.");
  1309.    pause("       BMAP<g>L<nn>.T3B  Partial hex.  Map data only.        No walls.");
  1310.    pause("       BMAP<g>L<nn>.T3C  Partial hex.  Object data only.     No 00's.");
  1311.    pause(" ");
  1312.    pause("  /C   BMAP<g>.COU       Table.        Count of map and object data by level");
  1313.    pause(" ");
  1314.    pause("  where: <g>  = game  number (1 to 6)");
  1315.    pause("         <nn> = level number (00 to 10)");
  1316.    pause(" ");
  1317.  
  1318.    return;
  1319. }
  1320.  
  1321.  
  1322. /*---------------------------------------------------------------------------*/
  1323.  
  1324.  
  1325. int make_map(int level, char *mapfile, char *objfile, char *combfile)
  1326. {
  1327.    /*****
  1328.    * Read level data
  1329.    *****/
  1330.    if (read_map(level))
  1331.       return;
  1332.  
  1333.    if (map_type == 0)
  1334.    {
  1335.       if (make_map2(0, level, combfile, map_data))
  1336.          return(1);
  1337.    }
  1338.    else
  1339.    {
  1340.       if (make_map2(2, level, combfile, map_data))
  1341.          return(1);
  1342.  
  1343.       if (make_map2(0, level, mapfile, map_data))
  1344.          return(1);
  1345.  
  1346.       if (make_map2(1, level, objfile, obj_data))
  1347.          return(1);
  1348.    }
  1349. }
  1350.  
  1351.  
  1352. /*---------------------------------------------------------------------------*/
  1353.  
  1354.  
  1355. int make_map2(int which, int level, char *outfile, word map_info[])
  1356. /************
  1357. * Make an output map file (map or objects)
  1358. ************/
  1359. {
  1360.    word count;
  1361.    int  start, mult;
  1362.    FILE *out_stream;
  1363.    char temp[10];
  1364.  
  1365.    /****
  1366.    * Open output map file
  1367.    ****/
  1368.    out_stream = fopen(outfile, "w");
  1369.    if (out_stream==NULL)
  1370.    {
  1371.       printf("Error opening file: %s\n", outfile);
  1372.       return(1);
  1373.    }
  1374.  
  1375.    /****
  1376.    * Create map
  1377.    ****/
  1378.    fprintf(out_stream, "\n");
  1379.  
  1380.    *map_title = NULL;
  1381.    strncat(map_title, lh.name, sizeof(lh.name));
  1382.  
  1383.    strcat(map_title, " (Episode ");
  1384.    sprintf(temp, "%d", episode_num);
  1385.    strcat(map_title, temp);
  1386.  
  1387.    strcat(map_title, ", Level ");
  1388.    sprintf(temp, "%d", level);
  1389.    strcat(map_title, temp);
  1390.    strcat(map_title, ")");
  1391.  
  1392.    switch (map_type)
  1393.    {
  1394.       case 0:
  1395.       case 1:
  1396.          if (double_width==0)
  1397.             mult = 1;
  1398.          else
  1399.             mult = 2;
  1400.          break;
  1401.       case 2:
  1402.       case 3:
  1403.          mult = 5;
  1404.          break;
  1405.    }
  1406.    start = ((lh.xsize * mult) - strlen(map_title)) / 2;
  1407.    for (count=0; count < start; count++)
  1408.        fprintf(out_stream, "%c", ' ');
  1409.    fprintf(out_stream, "%s\n", map_title);
  1410.  
  1411.    fprintf(out_stream, "\n");
  1412.    if (hex_scale_x)
  1413.    {
  1414.       print_xscale(out_stream, lh.xsize, map_type);
  1415.       fprintf(out_stream, "\n");
  1416.    }
  1417.  
  1418.    print_bytes(which, out_stream, map_info);
  1419.  
  1420.    if (hex_scale_x)
  1421.    {
  1422.       fprintf(out_stream, "\n");
  1423.       print_xscale(out_stream, lh.xsize, map_type);
  1424.    }
  1425.  
  1426.    if (fclose(out_stream))
  1427.    {
  1428.       printf("Error creating map file.\n");
  1429.       exit(1);
  1430.    }
  1431.    return(0);
  1432. }
  1433.  
  1434.  
  1435. /*---------------------------------------------------------------------------*/
  1436.  
  1437.  
  1438. int read_map(int level)
  1439. /******************
  1440. * Read the data for a level (map and objects)
  1441. *
  1442. * Params: level = 0 to n
  1443. ******************/
  1444. {
  1445.    word count, data, repeat;
  1446.    int  x, y;
  1447.    long offset;
  1448.    FILE *in_stream;
  1449.  
  1450.    /****
  1451.    * Open input map data file
  1452.    ****/
  1453.    in_stream = fopen(maptemp_filename, "rb");
  1454.    if (in_stream==NULL)
  1455.    {
  1456.       printf("Error opening file: %s\n", maptemp_filename);
  1457.       return(1);
  1458.    }
  1459.  
  1460.  
  1461.    /****
  1462.    * Read level header
  1463.    ****/
  1464.    if (seek_file(in_stream, map_offset[episode_num-1][level]))
  1465.       return(1);
  1466.  
  1467.    if (fread(&lh, sizeof(struct level_header), 1, in_stream) != 1)
  1468.    {
  1469.       printf("Error reading file: %s\n", maptemp_filename);
  1470.       return(1);
  1471.    }
  1472.  
  1473.  
  1474.    /****
  1475.    * Read map data.
  1476.    ****/
  1477.    if (seek_file(in_stream, lh.map_off))
  1478.       return(1);
  1479.    read_bytes(in_stream, map_data, lh.map_size);
  1480.  
  1481.  
  1482.    /****
  1483.    * Read object data.
  1484.    ****/
  1485.    if (seek_file(in_stream, lh.obj_off))
  1486.       return(1);
  1487.    read_bytes(in_stream, obj_data, lh.obj_size);
  1488.  
  1489.    fclose(in_stream);
  1490.  
  1491.    return(0);
  1492. }
  1493.  
  1494.  
  1495. /*---------------------------------------------------------------------------*/
  1496.  
  1497.  
  1498. void read_bytes(FILE *in_stream, word map_info[], word data_size)
  1499. /*****************
  1500. * Read data from the map file into array (map or objects).
  1501. *
  1502. * Params: data_size -- # bytes in data block (on disk)
  1503. *****************/
  1504. {
  1505.    word x, y, data, count, repeat;
  1506.    int  data_offset;
  1507.  
  1508.    byte low, high;
  1509.    byte *p1;
  1510.    word *p2;
  1511.    word *p3;
  1512.    int  counter;
  1513.    word after_decode, after_decompress;
  1514.  
  1515.    /****
  1516.    * Read data
  1517.    ****/
  1518.  
  1519.    if (comp_method==1)
  1520.    {
  1521.       // Read # bytes after decoding
  1522.       after_decode = (unsigned)fgetc(in_stream) + (unsigned)(fgetc(in_stream)*256);
  1523.    }
  1524.    // Read # of bytes after decompressing
  1525.    after_decompress = (unsigned)fgetc(in_stream) + (unsigned)(fgetc(in_stream)*256);
  1526.  
  1527.  
  1528.    // Read bytes from file
  1529.    if (fread(buf1_data, data_size, 1, in_stream) != 1)
  1530.    {
  1531.       printf("Error reading file: %s\n", maptemp_filename);
  1532.       exit(1);
  1533.    }
  1534.  
  1535.  
  1536.    if (comp_method==0)
  1537.    {
  1538.       // Version 1.0
  1539.  
  1540.       // Copy data into word buffer
  1541.  
  1542.       counter = data_size / 2;         // # words
  1543.  
  1544.       p1 = buf1_data;
  1545.       p2 = buf2_data;
  1546.  
  1547.       for (count=0; count < counter; count++)
  1548.       {
  1549.          *p2++ = *p1 + *(p1+1)*256;
  1550.          p1 += 2;
  1551.       }
  1552.    }
  1553.    else
  1554.    {
  1555.       // Version 1.1
  1556.  
  1557.       // Decode data
  1558.       counter = after_decode / 2;      // # words after decoding
  1559.  
  1560.       p1 = buf1_data;                  // source
  1561.       p2 = buf2_data;                  // dest
  1562.  
  1563.       do
  1564.       {
  1565.          low  = *p1++;                    // Get low byte of word
  1566.          high = *p1++;                    // Get high byte of word
  1567.  
  1568.          if (high == 0xa7)                // Test for A7 high byte
  1569.          {
  1570.             if (low == 0)                 // Is repeat count zero?
  1571.             {
  1572.                low  = *p1++;              // Actual low byte is in next byte
  1573.                data = low + high * 256;   // Create word
  1574.  
  1575.                *p2++ = data;              // Save word
  1576.                counter--;                 // One less word
  1577.             }
  1578.             else
  1579.             {
  1580.                // # of words is in low byte
  1581.  
  1582.                data = *p1++;              // Get offset (# of words)
  1583.                p3 = p2 - data;            // Calc temp source ptr = dest - offset
  1584.  
  1585.                counter -= low;            // Reduce # words
  1586.  
  1587.                for (count=0; count < low; count++)
  1588.                {
  1589.                   *p2++ = *p3++;
  1590.                }
  1591.             }
  1592.          }
  1593.          else if (high == 0xa8)           // Test for A8 high byte
  1594.          {
  1595.             if (low == 0)                 // Is repeat count zero?
  1596.             {
  1597.                low  = *p1++;              // Actual low byte is in next byte
  1598.                data = low + high * 256;   // Create word
  1599.  
  1600.                *p2++ = data;              // Save word
  1601.                counter--;                 // One less word
  1602.             }
  1603.             else
  1604.             {
  1605.                // # of words is in low byte
  1606.  
  1607.                data = *p1 + *(p1+1)*256;  // Get offset (# of words)
  1608.                p1 += 2;
  1609.  
  1610.                p3 = buf2_data + data - 1; // Calc temp source ptr = buf2 + # words - 1 word;
  1611.  
  1612.                counter -= low;            // Reduce # words
  1613.  
  1614.                for (count=0; count < low; count++)
  1615.                {
  1616.                   *p2++ = *p3++;
  1617.                }
  1618.             }
  1619.          }
  1620.          else
  1621.          {
  1622.             *p2++ = low + high * 256;     // Store word as is.
  1623.             counter--;                    // One less word
  1624.          }
  1625.       }
  1626.       while (counter > 0);
  1627.    }
  1628.  
  1629.  
  1630.    // Now we decompress the data
  1631.  
  1632.    counter = after_decompress / 2;        // # words after decompressing
  1633.  
  1634.    p2 = buf2_data;                        // Source
  1635.    p3 = map_info;                         // Destination
  1636.  
  1637.    do
  1638.    {
  1639.       data = *p2++;
  1640.  
  1641.       if (data == code_repeat)
  1642.       {
  1643.          repeat = *p2++;                     // # times to repeat next word
  1644.          data   = *p2++;                     // Word to repeat
  1645.  
  1646.          counter -= repeat;                  // Reduce # words
  1647.  
  1648.          for (count=0; count<repeat; count++)
  1649.          {
  1650.             *p3++ = data;                    // Repeat the word
  1651.          }
  1652.       }
  1653.       else
  1654.       {
  1655.          *p3++ = data;                       // Store the word
  1656.          counter--;                          // One less word
  1657.       }
  1658.    }
  1659.    while (counter > 0);
  1660. }
  1661.  
  1662.  
  1663. /*---------------------------------------------------------------------------*/
  1664.  
  1665.  
  1666. int seek_file(FILE *stream, long offset)
  1667. {
  1668.    if (fseek(stream, offset, SEEK_SET))
  1669.    {
  1670.       printf("Error seeking to offset %l\n", offset);
  1671.       return(1);
  1672.    }
  1673.    return(0);
  1674. }
  1675.  
  1676.  
  1677. /*---------------------------------------------------------------------------*/
  1678.  
  1679.  
  1680. void print_xscale(FILE *out_stream, word xsize, int map_type)
  1681. /*****************
  1682. * Print horizontal x scale on map
  1683. *****************/
  1684. {
  1685.    int count, row, start;
  1686.    char hex[8];
  1687.  
  1688.    start = 3;
  1689.    if (xsize > 0xf)
  1690.       start = 2;
  1691.    if (xsize > 0xff)
  1692.       start = 1;
  1693.    if (xsize > 0xfff)
  1694.       start = 0;
  1695.  
  1696.    for (row=start; row<4; row++)
  1697.    {
  1698.       if (hex_scale_y)
  1699.       {
  1700.          fprintf(out_stream, "   ");
  1701.  
  1702.          switch (map_type)
  1703.          {
  1704.             case 2:
  1705.             case 3:
  1706.                fprintf(out_stream, " ");
  1707.                break;
  1708.          }
  1709.       }
  1710.  
  1711.       for (count=0; count<xsize; count++)
  1712.       {
  1713.          switch (map_type)
  1714.          {
  1715.             case 2:
  1716.             case 3:
  1717.                fprintf(out_stream, "   ");
  1718.                break;
  1719.          }
  1720.  
  1721.          sprintf(hex, "%04x", count);
  1722.          fprintf(out_stream, "%c", *(hex+row));
  1723.          
  1724.          switch (map_type)
  1725.          {
  1726.             case 0:
  1727.             case 1:
  1728.                if (double_width)
  1729.                   fprintf(out_stream, " ");
  1730.                break;
  1731.             case 2:
  1732.             case 3:
  1733.                fprintf(out_stream, " ");
  1734.                break;
  1735.          }
  1736.       }
  1737.       fprintf(out_stream, "\n");
  1738.    }
  1739. }
  1740.  
  1741.  
  1742. /*---------------------------------------------------------------------------*/
  1743.  
  1744.  
  1745. void print_bytes(int which, FILE *out_stream, word map_info[])
  1746. /*****************
  1747. * Print map or object data
  1748. *****************/
  1749. {
  1750.    int  obj_suppress;
  1751.    int  obj_suppnext;
  1752.    word x, y, data, data1;
  1753.    int  data_offset;
  1754.    byte data_char, data_char2a, data_char2b;
  1755.    struct group_rec *og;
  1756.    struct group_rec *mg;
  1757.  
  1758.    obj_suppnext = 0;
  1759.  
  1760.    x = 0;
  1761.    y = 0;
  1762.  
  1763.    data_offset = 0;
  1764.    do
  1765.    {
  1766.       if (x==0 && hex_scale_y)
  1767.       {
  1768.          fprintf(out_stream, "%02x ", y);
  1769.  
  1770.          switch (map_type)
  1771.          {
  1772.             case 2:
  1773.             case 3:
  1774.                fprintf(out_stream, " ");
  1775.                break;
  1776.          }
  1777.       }
  1778.  
  1779.       data = map_info[data_offset];
  1780.  
  1781.       switch (map_type)
  1782.       {
  1783.          case 0:
  1784.          /******
  1785.          * Symbol map
  1786.          ******/
  1787.             /******
  1788.             * Combined map and object data
  1789.             ******/
  1790.             data1 = obj_data[data_offset];
  1791.  
  1792.             /****
  1793.             * Test for value out of range.
  1794.             * There seem to be values much larger than those needed for
  1795.             * objects, and they always seem to be followed by a second
  1796.             * value.  This obj_suppnext will prevent use of these pairs of
  1797.             * values and will use the map value instead of the object value.
  1798.             * I suspect the values have something to do with trying to
  1799.             * prevent modifications to the map data file.  If certain parts
  1800.             * of the data file are modified, the game will detect the
  1801.             * change and refuse to run.
  1802.             *****/
  1803.             if (data1 > MAX_VALUE && obj_suppnext == 0)
  1804.                obj_suppnext = 1;
  1805.  
  1806.             if (data1 != 0 && obj_suppnext == 0)
  1807.             {
  1808.                data1 = data;             /* save map data */
  1809.  
  1810.                data      = obj_data[data_offset];   /* get obj data */
  1811.                og = &obj_groups[obj_values[data].group];
  1812.                data_char   = og->character;
  1813.                data_char2a = og->character2a;
  1814.                data_char2b = og->character2b;
  1815.  
  1816.                /* Should we suppress object ? */
  1817.                obj_suppress = 0;
  1818.  
  1819.                if (og->type==OT_TRIVIAL && item_mode==2)
  1820.                {
  1821.                   /* Suppress trivial object */
  1822.                   obj_suppress = 1;
  1823.                }
  1824.                else if (og->type==OT_ENEMY && enemy_mode != 0)
  1825.                {
  1826.                   /* We want to display the enemy */
  1827.                   obj_suppress = 0;
  1828.                }
  1829.                else if (item_mode==0)
  1830.                {
  1831.                   /* Suppress all objects */
  1832.                   obj_suppress = 1;
  1833.                }
  1834.                else
  1835.                {
  1836.                   /* Display the object */
  1837.                   obj_suppress = 0;
  1838.                }
  1839.  
  1840.                if (obj_suppress)
  1841.                {
  1842.                   /* Suppress object, so use map data instead */
  1843.                   data = data1;
  1844.                   mg = &map_groups[map_values[data].group];
  1845.                   data_char   = mg->character;
  1846.                   data_char2a = mg->character2a;
  1847.                   data_char2b = mg->character2b;
  1848.                }
  1849.             }
  1850.             else
  1851.             {
  1852.                /* no object so use map character */
  1853.                mg = &map_groups[map_values[data].group];
  1854.                data_char   = mg->character;
  1855.                data_char2a = mg->character2a;
  1856.                data_char2b = mg->character2b;
  1857.  
  1858.                if (obj_suppnext)
  1859.                {
  1860.                   if (obj_suppnext >= 2)
  1861.                      obj_suppnext = 0;
  1862.                   else
  1863.                      obj_suppnext ++;
  1864.                }
  1865.             }
  1866.  
  1867.             if (double_width)
  1868.                fprintf(out_stream, "%c%c", data_char2a, data_char2b);
  1869.             else
  1870.                fprintf(out_stream, "%c", data_char);
  1871.             break;
  1872.  
  1873.          case 1:
  1874.          /******
  1875.          * Symbol map
  1876.          ******/
  1877.             switch (which)
  1878.             {
  1879.                case 0:
  1880.                   /******
  1881.                   * Map data only
  1882.                   ******/
  1883.                   mg = &map_groups[map_values[data].group];
  1884.                   if (double_width)
  1885.                      fprintf(out_stream, "%c%c", mg->character2a, mg->character2b);
  1886.                   else
  1887.                      fprintf(out_stream, "%c", mg->character);
  1888.                   break;
  1889.  
  1890.                case 1:
  1891.                   /******
  1892.                   * Object data only
  1893.                   ******/
  1894.  
  1895.                   /****
  1896.                   * Test for value out of range.
  1897.                   * (See map type 0 above)
  1898.                   *****/
  1899.                   if (data > MAX_VALUE && obj_suppnext == 0)
  1900.                      obj_suppnext = 1;
  1901.  
  1902.                   og = &obj_groups[obj_values[data].group];
  1903.  
  1904.                   if ((item_mode==2 && og->type==OT_TRIVIAL) || obj_suppnext != 0)
  1905.                   {
  1906.                      if (double_width)
  1907.                         fprintf(out_stream, "  ");  /* suppress trivial object */
  1908.                      else
  1909.                         fprintf(out_stream, " ");   /* suppress trivial object */
  1910.  
  1911.                      if (obj_suppnext)
  1912.                      {
  1913.                         if (obj_suppnext >= 2)
  1914.                            obj_suppnext = 0;
  1915.                         else
  1916.                            obj_suppnext ++;
  1917.                      }
  1918.                   }
  1919.                   else
  1920.                   {
  1921.                      if (double_width)
  1922.                         fprintf(out_stream, "%c%c", og->character2a, og->character2b);
  1923.                      else
  1924.                         fprintf(out_stream, "%c", og->character);
  1925.                   }
  1926.                   break;
  1927.  
  1928.                case 2:
  1929.                   /******
  1930.                   * Combined map and object data
  1931.                   ******/
  1932.                   if (obj_data[data_offset]!=0)
  1933.                   {
  1934.                      data1 = data;             /* save map data */
  1935.  
  1936.                      data      = obj_data[data_offset];   /* get obj data */
  1937.  
  1938.                      /****
  1939.                      * Test for value out of range.
  1940.                      * (See map type 0 above)
  1941.                      *****/
  1942.                      if (data > MAX_VALUE && obj_suppnext == 0)
  1943.                         obj_suppnext = 1;
  1944.  
  1945.                      og = &obj_groups[obj_values[data].group];
  1946.                      data_char   = og->character;
  1947.                      data_char2a = og->character2a;
  1948.                      data_char2b = og->character2b;
  1949.  
  1950.                      if ((item_mode==2 && og->type==OT_TRIVIAL) || obj_suppnext != 0)
  1951.                      {
  1952.                         /* Is a trivial object, so use map data instead */
  1953.                         data = data1;
  1954.                         mg = &map_groups[map_values[data].group];
  1955.                         data_char   = mg->character;
  1956.                         data_char2a = mg->character2a;
  1957.                         data_char2b = mg->character2b;
  1958.                      }
  1959.                   }
  1960.                   else
  1961.                   {
  1962.                      /* no object so use map character */
  1963.                      mg = &map_groups[map_values[data].group];
  1964.                      data_char   = mg->character;
  1965.                      data_char2a = mg->character2a;
  1966.                      data_char2b = mg->character2b;
  1967.                   }
  1968.  
  1969.                   if (double_width)
  1970.                      fprintf(out_stream, "%c%c", data_char2a, data_char2b);
  1971.                   else
  1972.                      fprintf(out_stream, "%c", data_char);
  1973.  
  1974.                   if (obj_suppnext)
  1975.                   {
  1976.                      if (obj_suppnext >= 2)
  1977.                         obj_suppnext = 0;
  1978.                      else
  1979.                         obj_suppnext ++;
  1980.                   }
  1981.  
  1982.                   break;
  1983.             }
  1984.             break;
  1985.          case 2:
  1986.             /******
  1987.             * Hex map unfiltered full dump.
  1988.             ******/
  1989.             switch (which)
  1990.             {
  1991.                case 2:
  1992.                   /****
  1993.                   * Combined map and object data
  1994.                   * We want overlay any objects on the map data
  1995.                   ****/
  1996.                   if (obj_data[data_offset]!=0)
  1997.                   {
  1998.                      data = obj_data[data_offset];   /* dispay object value */
  1999.                   }
  2000.                   break;
  2001.             }
  2002.             fprintf(out_stream, "%4x ", data);
  2003.             break;
  2004.  
  2005.          case 3:
  2006.          /******
  2007.          * Hex maps, filtered, partial dump
  2008.          ******/
  2009.             og = &obj_groups[obj_values[data].group];
  2010.             mg = &map_groups[map_values[data].group];
  2011.  
  2012.             switch (which)
  2013.             {
  2014.                case 0:
  2015.                   /******
  2016.                   * Map data only
  2017.                   * We want to show all but walls.
  2018.                   ****/
  2019.                   if (mg->type==MT_WALL)
  2020.                   {
  2021.                      data = 0;  /* suppress walls */
  2022.                   }
  2023.                   break;
  2024.  
  2025.                case 1:
  2026.                   /****
  2027.                   * Object data only
  2028.                   * We want to show objects only.
  2029.                   ****/
  2030.                   if (item_mode==2 && og->type==OT_TRIVIAL)
  2031.                   {
  2032.                      /* Is a trivial object, so ignore */
  2033.                      data = 0;
  2034.                   }
  2035.                   break;
  2036.  
  2037.                case 2:
  2038.                   /****
  2039.                   * Combined map and object data
  2040.                   * We want to show any objects, all walls, and no floors.
  2041.                   ****/
  2042.                   if (obj_data[data_offset]!=0)
  2043.                   {
  2044.                      data = obj_data[data_offset];   /* dispay object value */
  2045.  
  2046.                      og = &obj_groups[obj_values[data].group];
  2047.                      if (item_mode==2 && og->type==OT_TRIVIAL)
  2048.                      {
  2049.                         /* Is a trivial object, so ignore */
  2050.                         data = 0;
  2051.                      }
  2052.                   }
  2053.                   else if (mg->type==MT_FLOOR)
  2054.                   {
  2055.                      data = 0;  /* suppress hex value */
  2056.                   }
  2057.                   break;
  2058.             }
  2059.  
  2060.             if (data==0)
  2061.                fprintf(out_stream, "     ");
  2062.             else
  2063.                fprintf(out_stream, "%4x ", data);
  2064.  
  2065.             break;
  2066.       }
  2067.  
  2068.       x++;
  2069.       data_offset++;
  2070.  
  2071.       if (x >= lh.xsize)
  2072.       {
  2073.          if (hex_scale_y)
  2074.             fprintf(out_stream, " %02x", y);
  2075.  
  2076.          fprintf(out_stream, "\n");
  2077.          x = 0;
  2078.          y++;
  2079.       }
  2080.    }
  2081.    while (y < lh.ysize);
  2082. }
  2083.  
  2084.  
  2085. /*---------------------------------------------------------------------------*/
  2086.  
  2087.  
  2088. int count_data()
  2089. {
  2090.    int i, j;
  2091.    unsigned long prvs_offset;
  2092.    FILE *out_stream;
  2093.  
  2094.  
  2095.    out_stream = fopen(count_filename, "w");
  2096.    if (out_stream==NULL)
  2097.    {
  2098.       printf("Error opening %s\n", count_filename);
  2099.       return(1);
  2100.    }
  2101.  
  2102.    fprintf(out_stream, "\nBlake Stone AOG:  Episode %d\n\n", episode_num);
  2103.  
  2104.  
  2105.    /********
  2106.    * Map data
  2107.    ********/
  2108.  
  2109.    /* Reset totals */
  2110.    for (i=0; i<512; i++)
  2111.    {
  2112.       totals[i] = 0;
  2113.  
  2114.       for (j=0; j < MAP_MAXLEVELS; j++)
  2115.          counts[j][i] = 0;
  2116.    }
  2117.  
  2118.    /* Count map data */
  2119.    prvs_offset = 0;
  2120.    for (i=0; i<MAP_MAXLEVELS; i++)
  2121.    {
  2122.       if (map_offset[episode_num-1][i]!=0 && map_offset[episode_num-1][i]!=0xffffffff
  2123.        && map_offset[episode_num-1][i]>prvs_offset)
  2124.       {
  2125.          prvs_offset = map_offset[episode_num-1][i];
  2126.          if (read_map(i))
  2127.             return(1);
  2128.          count_level(i, map_data);
  2129.       }
  2130.    }
  2131.  
  2132.    /* Print results */
  2133.    fprintf(out_stream, "\nMap data\n\n");
  2134.    print_counts(out_stream);
  2135.  
  2136.    /********
  2137.    * Object data
  2138.    ********/
  2139.  
  2140.    /* Reset totals */
  2141.    for (i=0; i<512; i++)
  2142.    {
  2143.       totals[i] = 0;
  2144.  
  2145.       for (j=0; j < MAP_MAXLEVELS; j++)
  2146.          counts[j][i] = 0;
  2147.    }
  2148.    /* Count object data */
  2149.    prvs_offset = 0;
  2150.    for (i=0; i<MAP_MAXLEVELS; i++)
  2151.    {
  2152.       if (map_offset[episode_num-1][i]!=0 && map_offset[episode_num-1][i]!=0xffffffff
  2153.        && map_offset[episode_num-1][i]>prvs_offset)
  2154.       {
  2155.          prvs_offset = map_offset[episode_num-1][i];
  2156.          if (read_map(i))
  2157.             return(1);
  2158.          count_level(i, obj_data);
  2159.       }
  2160.    }
  2161.  
  2162.    /* Print results */
  2163.    fprintf(out_stream, "\nObject data\n\n");
  2164.    print_counts(out_stream);
  2165.  
  2166.  
  2167.  
  2168.    fclose(out_stream);
  2169.  
  2170.    return(0);
  2171. }
  2172.  
  2173.  
  2174. /*---------------------------------------------------------------------------*/
  2175.  
  2176.  
  2177. void print_counts(FILE *out_stream)
  2178. {
  2179.    int i, j;
  2180.  
  2181.  
  2182.    fprintf(out_stream, "       TOTAL");
  2183.    for (j=0; j<MAP_MAXLEVELS; j++)
  2184.       fprintf(out_stream, " %5d", j);
  2185.    fprintf(out_stream, "\n");
  2186.  
  2187.    fprintf(out_stream, "      ======");
  2188.    for (j=0; j<MAP_MAXLEVELS; j++)
  2189.       fprintf(out_stream, " -----");
  2190.    fprintf(out_stream, "\n");
  2191.  
  2192.  
  2193.    for (i=0; i<512; i++)
  2194.    {
  2195.       if (totals[i] > 0)
  2196.       {
  2197.          fprintf(out_stream, "%4x: %6u", i, totals[i]);
  2198.  
  2199.          for (j=0; j<MAP_MAXLEVELS; j++)
  2200.          {
  2201.             if (counts[j][i] > 0)
  2202.                 fprintf(out_stream, " %5u", counts[j][i]);
  2203.             else
  2204.                 fprintf(out_stream, "      ");
  2205.          }
  2206.  
  2207.          fprintf(out_stream, "\n");
  2208.       }
  2209.    }
  2210.    return;
  2211. }
  2212.  
  2213.  
  2214. /*---------------------------------------------------------------------------*/
  2215.  
  2216.  
  2217. void count_level(int level, word map_info[])
  2218. {
  2219.    int i;
  2220.    int data_offset;
  2221.  
  2222.    word x, y, data;
  2223.  
  2224.    x = 0;
  2225.    y = 0;
  2226.  
  2227.    data_offset = 0;
  2228.    do
  2229.    {
  2230.       data = map_info[data_offset];
  2231.  
  2232.       if (data >= 0 && data <= 0x1ff)
  2233.       {
  2234.          counts[level][data] ++;
  2235.          totals[data] ++;
  2236.       }
  2237. //    else
  2238. //    {
  2239. //       printf("Level=%2d, Data offset=%5d, X=%2u, Y=%2u, Data=%4x\n", level,
  2240. //          data_offset, x, y, data);
  2241. //    }
  2242.  
  2243.       x++;
  2244.       data_offset++;
  2245.  
  2246.       if (x >= lh.xsize)
  2247.       {
  2248.          x = 0;
  2249.          y++;
  2250.       }
  2251.    }
  2252.    while (y < lh.ysize);
  2253.  
  2254.    return;
  2255. }
  2256.  
  2257.  
  2258. /*---------------------------------------------------------------------------*/
  2259.  
  2260.  
  2261. int hextoi(char *hex)
  2262. {
  2263.    char *p2, ch;
  2264.    long mult;
  2265.    int  result;
  2266.    int  digit;
  2267.  
  2268.    mult = 1;
  2269.    result = 0;
  2270.  
  2271.    if (*hex == NULL)
  2272.       return(0);
  2273.  
  2274.    p2 = hex;
  2275.  
  2276.    // Advance to last digit
  2277.    while (*p2)
  2278.       p2++;
  2279.    p2--;
  2280.  
  2281.    do
  2282.    {
  2283.       ch = toupper(*p2);
  2284.  
  2285.       if (isxdigit(ch)==0)
  2286.          ch = '0';
  2287.  
  2288.       if (ch >= '0' && ch <= '9')
  2289.       {
  2290.          digit = ch - '0';
  2291.       }
  2292.       else
  2293.       {
  2294.          digit = ch - 'A' + 10;
  2295.       }
  2296.  
  2297.       result += (digit * mult);
  2298.  
  2299.       mult *= 16;
  2300.    }
  2301.    while (p2-- != hex);
  2302.  
  2303.    return(result);
  2304. }
  2305.  
  2306.  
  2307. /*---------------------------------------------------------------------------*/
  2308.  
  2309.  
  2310. void print_legend()
  2311. {
  2312.    int ii, jj;
  2313.    int count1, count2, count3;
  2314.    int start2, start3;
  2315.    FILE *stream;
  2316.  
  2317.    count1 = 0;
  2318.  
  2319.    // Create arrays of pointers to structures, eliminate items that will not
  2320.    // print on legend, calculate # elements to print in each column.
  2321.    for (ii=MIN_GROUP; ii<=MAX_GROUP; ii++)
  2322.    {
  2323.       if (map_groups[ii].legend == 'Y')
  2324.       {
  2325.          sortgroups[count1++] = &map_groups[ii];
  2326.       }
  2327.    }
  2328.    for (ii=MIN_GROUP; ii<=MAX_GROUP; ii++)
  2329.    {
  2330.       if (obj_groups[ii].legend == 'Y')
  2331.       {
  2332.          sortgroups[count1++] = &obj_groups[ii];
  2333.       }
  2334.    }
  2335.  
  2336.    // Calculate # to print in each column
  2337.    start2 = 0;
  2338.    if (count1 > 60)
  2339.    {
  2340.       // More than one column
  2341.       if (count1 <= 120)
  2342.       {
  2343.          // But <= 2 columns...Fill left column first.
  2344.          count2 = 60;
  2345.          start3 = count2;
  2346.          count3 = count1 - count2;
  2347.       }
  2348.       else
  2349.       {
  2350.          // More than 2 columns...Split evenly into two long columns.
  2351.          count2 = (count1+1)/ 2;
  2352.          start3 = count2;
  2353.          count3 = count1 - count2;
  2354.       }
  2355.    }
  2356.    else
  2357.    {
  2358.       // <= 1 column
  2359.       count2 = count1;
  2360.       start3 = 0;
  2361.       count3 = 0;
  2362.    }
  2363.  
  2364.  
  2365.    // Sort list
  2366.    vSort_List(sortgroups, count1);
  2367.  
  2368.    // Open print file
  2369.    sprintf(legend_filename, "%s%d%s", FILE_LEGEND, episode_num, FILE_LEGENDEXT);
  2370.    stream = fopen(legend_filename, "w");
  2371.    if (stream == NULL)
  2372.    {
  2373.       printf("Unable to create legend file.\n");
  2374.       exit(1);
  2375.    }
  2376.  
  2377.    /*
  2378.    *
  2379.    * 1
  2380.    * 2 Map legend
  2381.    * 3
  2382.    * 4 SYM
  2383.    * 5 ===
  2384.    * 6
  2385.    *
  2386.    *60
  2387.    */
  2388.  
  2389.    fprintf(stream, "\n%20sMap Legend for Blake Stone AOG Episode %d\n", " ", episode_num);
  2390.  
  2391.    fprintf(stream, "\nSYM             DESCRIPTION            ");
  2392.    if (count3 > 0)
  2393.       fprintf(stream, " SYM             DESCRIPTION\n");
  2394.    else
  2395.       fprintf(stream, "\n");
  2396.  
  2397.    fprintf(stream,   "=== ===================================");
  2398.    if (count3 > 0)
  2399.       fprintf(stream, " === ===================================\n");
  2400.    else
  2401.       fprintf(stream, "\n");
  2402.  
  2403.    // Print details
  2404.    for (ii=start2, jj=start3; ii < count2 || jj < start3+count3; ii++, jj++)
  2405.    {
  2406.       if (ii < count2)
  2407.       {
  2408.          if (double_width)
  2409.             fprintf(stream, "%c%c", sortgroups[ii]->character2a, sortgroups[ii]->character2b);
  2410.          else
  2411.             fprintf(stream, " %c", sortgroups[ii]->character);
  2412.          fprintf(stream, "  %-35s ", sortgroups[ii]->description);
  2413.       }
  2414.       else
  2415.       {
  2416.          fprintf(stream, "%41s", " ");
  2417.       }
  2418.  
  2419.       if (jj < start3+count3)
  2420.       {
  2421.          if (double_width)
  2422.             fprintf(stream, "%c%c", sortgroups[jj]->character2a, sortgroups[jj]->character2b);
  2423.          else
  2424.             fprintf(stream, " %c", sortgroups[jj]->character);
  2425.          fprintf(stream, "  %-35s", sortgroups[jj]->description);
  2426.       }
  2427.  
  2428.       fprintf(stream, "\n");
  2429.    }
  2430.  
  2431.    fclose(stream);
  2432.    return;
  2433. }
  2434.  
  2435.  
  2436. /*---------------------------------------------------------------------------*/
  2437.  
  2438.  
  2439. void vSort_List(struct group_rec *apsList[], int iLength)
  2440. /***************************
  2441. * Sort a list of group structures by symbol + description (ascending).
  2442. *
  2443. * Parameters: apsList[]: Array of pointers to group structures
  2444. *             iLength  : Length of array (number of elements).
  2445. *
  2446. * Returns   : nothing
  2447. ***************************/
  2448. {
  2449.    int  iCount1;   /* outer loop counter (number of passes through list) */
  2450.    int  iCount2;   /* inner loop counter (lower element # to test) */
  2451.    int  iSwaps;    /* swap flag (0=no swaps this pass, 1=swap ocurred) */
  2452.    int  iSwapit;
  2453.    byte char1a, char2a;
  2454.    byte char1b, char2b;
  2455.  
  2456.    struct group_rec *psTemp; /* holds pointer during swap of pointers */
  2457.  
  2458.    struct group_rec *p1;
  2459.    struct group_rec *p2;
  2460.  
  2461.    /*****
  2462.    * Sort the list by symbol + description (ascending order)
  2463.    ******/
  2464.    iSwapit = 0;
  2465.  
  2466.    /* Reduce # elements to test each loop */
  2467.    for (iCount1 = iLength-1; iCount1 > 0; iCount1 --)
  2468.    {
  2469.       iSwaps = 0;
  2470.  
  2471.       /***
  2472.       * Loop to test all elements not yet sorted
  2473.       ****/
  2474.       for (iCount2 = 0; iCount2 < iCount1; iCount2 ++)
  2475.       {
  2476.          p1 = apsList[iCount2];
  2477.          p2 = apsList[iCount2+1];
  2478.  
  2479.          if (double_width)
  2480.          {
  2481.             char1a = toupper(p1->character2a);
  2482.             char1b = toupper(p1->character2b);
  2483.             char2a = toupper(p2->character2a);
  2484.             char2b = toupper(p2->character2b);
  2485.  
  2486.             if (char1a > char2a)
  2487.             {
  2488.                iSwapit = 1;
  2489.             }
  2490.             else
  2491.             {
  2492.                if ((char1a == char2a) && (char1b >  char2b))
  2493.                {
  2494.                   iSwapit = 1;
  2495.                }
  2496.                else
  2497.                {
  2498.                   if ((char1a == char2a) && (char1b == char2b) &&
  2499.                       (strcmp(p1->description, p2->description) > 0))
  2500.                   {
  2501.                      iSwapit = 1;
  2502.                   }
  2503.                }
  2504.             }
  2505.          }
  2506.          else
  2507.          {
  2508.             char1a = toupper(p1->character);
  2509.             char2a = toupper(p2->character);
  2510.  
  2511.             if (char1a > char2a)
  2512.             {
  2513.                iSwapit = 1;
  2514.             }
  2515.             else
  2516.             {
  2517.                if ((char1a == char2a) &&
  2518.                    (strcmp(p1->description, p2->description) > 0))
  2519.                {
  2520.                   iSwapit = 1;
  2521.                }
  2522.             }
  2523.          }
  2524.  
  2525.          if (iSwapit)
  2526.          {
  2527.             iSwapit = 0;
  2528.             iSwaps  = 1;
  2529.  
  2530.             /***
  2531.             * Swap pointers
  2532.             ****/
  2533.             psTemp             = apsList[iCount2];
  2534.             apsList[iCount2]   = apsList[iCount2+1];
  2535.             apsList[iCount2+1] = psTemp;
  2536.          }
  2537.       }
  2538.  
  2539.       if (iSwaps==0)  /* If no swaps have ocurred then we are done */
  2540.          break;
  2541.    }
  2542.    return;
  2543. }
  2544.  
  2545.  
  2546. /*---------------------------------------------------------------------------*/
  2547.  
  2548.  
  2549. int read_hdr_info(FILE *stream, int max_episodes)
  2550. /****************
  2551. * Read data from the map header file (tells us the offset of each map
  2552. * in the actual map data file)
  2553. *
  2554. * Returns the number of episodes found in the file.
  2555. ****************/
  2556. {
  2557.    unsigned long prvs_offset;
  2558.    unsigned long offset, save_offset;
  2559.    int num_read, good_offset, num_episodes;
  2560.    word data, count, count2;
  2561.  
  2562.  
  2563.  
  2564.    /* Get initial word (ABCD) */
  2565.    code_repeat = (unsigned)fgetc(stream) + (unsigned)(fgetc(stream)*256);
  2566.  
  2567.  
  2568.    /* Skip over any 0 or ffffffff offset (in case the .BS6 file containing
  2569.       only episodes 4 to 6 does not store the offsets right at the beginning
  2570.       of the file...(this was not known at the time this program was written))
  2571.    */
  2572.    while (feof(stream)==0)
  2573.    {
  2574.       save_offset = ftell(stream);
  2575.  
  2576.       num_read = fread(&offset, 4, 1, stream);
  2577.       if (num_read != 1)
  2578.          break;
  2579.  
  2580.       if (offset!=0 && offset!=0xffffffff)
  2581.       {
  2582.          fseek(stream, save_offset, SEEK_SET);  // So we can reread map offset
  2583.          break;
  2584.       }
  2585.    }
  2586.  
  2587.    /* Get file offsets to maps */
  2588.    num_episodes = 0;
  2589.    prvs_offset = 0;
  2590.    for (count=0; count<max_episodes && feof(stream)==0; count++)
  2591.    {
  2592.       good_offset = 0;
  2593.  
  2594.       for (count2=0; count2<MAP_MAXLEVELS && feof(stream)==0; count2++)
  2595.       {
  2596.          num_read = fread(&offset, 4, 1, stream);
  2597.          if (num_read != 1)
  2598.             break;
  2599.  
  2600.          map_offset[count][count2] = offset;
  2601.  
  2602.          if (offset!=0 && offset!=0xffffffff && offset>prvs_offset)
  2603.          {
  2604.             good_offset = 1;
  2605.             prvs_offset = offset;
  2606.          }
  2607.       }
  2608.       if (good_offset)
  2609.          num_episodes++;  // Count # episodes with at least one valid offset
  2610.    }
  2611.  
  2612.    return(num_episodes);
  2613. }
  2614.  
  2615.  
  2616. /*---------------------------------------------------------------------------*/
  2617.  
  2618.  
  2619. void read_header_files()
  2620. {
  2621.    int count, count2;
  2622.    FILE *temp;
  2623.  
  2624.    strcpy(maphead_filename, FILE_MAPHEAD);
  2625.    strcat(maphead_filename, FILE_MAPEXT);
  2626.    strcat(maphead_filename, "6");
  2627.  
  2628.    temp = fopen(maphead_filename, "rb");
  2629.    if (temp!=NULL)
  2630.    {
  2631.       /***
  2632.       * File should contain data for all 6 episodes
  2633.       ****/
  2634.       num_episodes = read_hdr_info(temp, 6);
  2635.       fclose(temp);
  2636.  
  2637.       ep16_flag = 1;
  2638.    }
  2639.    else
  2640.    {
  2641.       /*****
  2642.       * Was no MAPHEAD.BS6 file, so try for just MAPHEAD.BS3 (first 3 episodes)
  2643.       ******/
  2644.       strcpy(maphead_filename, FILE_MAPHEAD);
  2645.       strcat(maphead_filename, FILE_MAPEXT);
  2646.       strcat(maphead_filename, "3");
  2647.  
  2648.       temp = fopen(maphead_filename, "rb");
  2649.       if (temp!=NULL)
  2650.       {
  2651.          /* Header file should contain map offsets for episodes 1 to 3 */
  2652.          num_episodes = read_hdr_info(temp, 6);
  2653.          fclose(temp);
  2654.          ep13_flag = 1;
  2655.       }
  2656.       else
  2657.       {
  2658.          /*****
  2659.          * Was no MAPHEAD.BS3 file, so try for just MAPHEAD.BS1 (episode
  2660.          * 1 only)
  2661.          ******/
  2662.          strcpy(maphead_filename, FILE_MAPHEAD);
  2663.          strcat(maphead_filename, FILE_MAPEXT);
  2664.          strcat(maphead_filename, "1");
  2665.  
  2666.          temp = fopen(maphead_filename, "rb");
  2667.          if (temp!=NULL)
  2668.          {
  2669.             /* Header file should contain map offsets for episode 1 */
  2670.             num_episodes = read_hdr_info(temp, 6);
  2671.             fclose(temp);
  2672.             ep11_flag = 1;
  2673.          }
  2674.          else
  2675.          {
  2676.             /*****
  2677.             * Could not find MAPHEAD.BS6, .BS3, or .BS1
  2678.             ******/
  2679.             printf("Unable to locate %s%s6, %s%s3, or %s%s1\n",
  2680.                  FILE_MAPHEAD, FILE_MAPEXT, FILE_MAPHEAD, FILE_MAPEXT,
  2681.                  FILE_MAPHEAD, FILE_MAPEXT);
  2682.             exit(1);
  2683.          }
  2684.       }
  2685.    }
  2686.  
  2687.    if (num_episodes==0)
  2688.    {
  2689.       printf("No map offsets found\n");
  2690.       exit(1);
  2691.    }
  2692.    if (num_episodes > MAP_MAXEPISODES)
  2693.       num_episodes = MAP_MAXEPISODES;
  2694.  
  2695.    return;
  2696. }
  2697.  
  2698.